﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System.Text;
using System.Globalization;

namespace Microsoft.ResourceManagement.Samples.ResourceManagementClient.ResourceManagement
{
    /// <summary>
    /// This class represents an attribute in the resource management system.
    /// An attribute is either single or multi-valued.
    /// A multi-valued attribute is a collection of single-valued attributes.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
    public class ResourceManagementAttribute
    {
        private List<Object> attributeMultiValue;
        private List<ResourceManagementAttributeChange> attributeChangeList;
        private bool isMultiValued;
        private String attributeName;
        private bool attributeValueChanged;
        private Object attributeSingleValue;

        /// <summary>
        /// Constructs a new empty multi-valued attribute with a blank name.
        /// </summary>
        public ResourceManagementAttribute()
        {
            this.attributeChangeList = new List<ResourceManagementAttributeChange>();
            this.attributeMultiValue = new List<object>();
        }
        /// <summary>
        /// Constructs a new single-valued attribute with the given name and starting value
        /// </summary>
        /// <param name="name">The attribute name.</param>
        /// <param name="value">The attribute value.</param>
        public ResourceManagementAttribute(String name, Object value)
            : this(false, name, value)
        {
        }
        /// <summary>
        /// Constructs a new single- or multi-valued attribute with the given name and null starting value.
        /// </summary>
        /// <param name="isMultiValue">Whether the attribute is multi-valued.</param>
        /// <param name="name">The name of the attribute.</param>
        public ResourceManagementAttribute(bool isMultiValue, String name)
            : this(isMultiValue, name, null)
        {

        }
        /// <summary>
        /// Constructs a new single- of multi-valued attribute with the given name and starting value.
        /// </summary>
        /// <param name="isMultiValue">Whether the attribute is multi-valued.</param>
        /// <param name="name">The name of the attribute.</param>
        /// <param name="value">The starting value of the attribute.</param>
        public ResourceManagementAttribute(bool isMultiValue, String name, Object value)
            : this()
        {
            this.attributeName = name;
            if (isMultiValue == false)
            {
                this.attributeMultiValue = null;
                this.isMultiValued = false;
                this.attributeSingleValue = value;
            }
            else
            {
                this.attributeMultiValue = new List<object>();
                this.isMultiValued = true;
                this.attributeSingleValue = null;
            }
        }

        /// <summary>
        /// True if this attribute is multi-valued
        /// </summary>
        public bool IsMultiValued
        {
            get
            {
                return this.isMultiValued;
            }
        }

        /// <summary>
        /// The name of the attribute.
        /// </summary>
        public String AttributeName
        {
            get
            {
                return this.attributeName;
            }
        }

        /// <summary>
        /// True if the value of this attribute has changed.
        /// </summary>
        public bool IsValueChanged
        {
            get
            {
                return this.attributeValueChanged;
            }
        }

        /// <summary>
        /// The single value of this attribute.
        /// Throws exception if this is multi-valued attribute.
        /// </summary>
        public Object AttributeSingleValue
        {
            get
            {
                if (isMultiValued)
                    throw new InvalidOperationException("Cannot access a single value of a multi-valued attribute");
                return this.attributeSingleValue;
            }
            set
            {
                ReplaceAttributeSingleValue(value);
            }
        }

        /// <summary>
        /// Sets the single valued-attribute to a new value.
        /// </summary>
        /// <param name="newObject">The new value of the attribute</param>
        public void ReplaceAttributeSingleValue(Object newValue)
        {
            if (isMultiValued)
                throw new InvalidOperationException("Cannot access a single value of a multi-valued attribute");
            if (null == this.attributeSingleValue)
            {
                this.attributeSingleValue = newValue;
                this.attributeValueChanged = true;
            }
            else
            {
                lock (this.attributeSingleValue)
                {
                    this.attributeValueChanged = true;
                    this.attributeSingleValue = newValue;
                }
            }
        }

        /// <summary>
        /// The read-only collection of multi-valued attribute.
        /// Throws exception if this is a single-valued attribute.
        /// </summary>
        public ReadOnlyCollection<Object> AttributeMultiValue
        {
            get
            {
                if (isMultiValued == false)
                    throw new InvalidOperationException("Cannot access multi-value of a single-valued attribute");
                return new ReadOnlyCollection<object>(this.attributeMultiValue);
            }
        }

        /// <summary>
        /// The values list that have been added or removed from this multi-valued attribute.
        /// Throws exception if this is a single-valued attribute.
        /// </summary>
        public ReadOnlyCollection<ResourceManagementAttributeChange> AttributeChangeList
        {
            get
            {
                if (isMultiValued == false)
                    throw new InvalidOperationException("Cannot access multi-value of a single-valued attribute");
                return new ReadOnlyCollection<ResourceManagementAttributeChange>(this.attributeChangeList);
            }
        }

        /// <summary>
        /// Removes the given value from the multi-valued attribute.
        /// Throws exception if this is a single-valued attribute.
        /// </summary>
        /// <param name="value">The value to remove.</param>
        /// <returns>True if the value was removed.</returns>
        public bool RemoveMultiValue(Object value)
        {
            if (isMultiValued == false)
                throw new InvalidOperationException("Cannot access multi-value of a single-valued attribute");
            lock (this.attributeMultiValue)
            {
                if (this.attributeMultiValue.Remove(value))
                {
                    lock (this.attributeChangeList)
                    {
                        ResourceManagementAttributeChange change = new ResourceManagementAttributeChange();
                        change.Operation = Constants.ExtensionIdentityManagementOperationsChangeDelete;
                        change.Value = value;
                        this.attributeChangeList.Add(change);
                        this.attributeValueChanged = true;
                    }
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        /// <summary>
        /// Adds the given value to the multi-valued attribute.
        /// Throws exception if this is a single-valued attribute.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool AddMultiValue(Object value)
        {
            if (isMultiValued == false)
                throw new InvalidOperationException("Cannot access multi-value of a single-valued attribute");
            lock (this.attributeMultiValue)
            {
                if (this.attributeMultiValue.Contains(value) == false)
                {
                    this.attributeMultiValue.Add(value);
                    lock (this.attributeChangeList)
                    {
                        ResourceManagementAttributeChange change = new ResourceManagementAttributeChange();
                        change.Operation = Constants.ExtensionIdentityManagementOperationsChangeAdd;
                        change.Value = value;
                        this.attributeChangeList.Add(change);
                        this.attributeValueChanged = true;
                    }
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        public override string ToString()
        {
            if (this.isMultiValued)
            {
                String[] strValues = new String[this.attributeMultiValue.Count];
                for (int i = 0; i < strValues.Length; i++)
                {
                    strValues[i] = this.attributeMultiValue[i].ToString();
                }
                return String.Format(CultureInfo.CurrentCulture, "[{0}]", String.Join(",", strValues));
            }
            else
            {
                return this.attributeSingleValue.ToString();
            }
        }

        /// <summary>
        /// Marks the attribute as unchanged.  Does not change the value.
        /// </summary>
        public void ClearIsChanged()
        {
            this.attributeValueChanged = false;
            this.attributeChangeList.Clear();
        }
    }

    public class ResourceManagementAttributeChange
    {
        private string operation;
        private object objectValue;

        public ResourceManagementAttributeChange()
        {
        }

        public string Operation
        {
            get { return this.operation; }
            set { this.operation = value; }
        }

        public object Value
        {
            get { return this.objectValue; }
            set { this.objectValue = value; }
        }
    }
}
